/**
 * Manage photo display
 * @param {View|EmbeddedView} shell
 * @constructor
 */
function PhotoMediator(shell)
{
	"use strict";

	/** @type {PhotoMediator} */
	var _this = this;
	/** @type {Boolean} */
	var _zoomEnabled = false;	// enable zoom if image larger than window
	/** @type {Boolean} */
	var _fitToScreen = true;
	/** @type {jQuery} */
	var $frame = $('#photo');
	/** @type {jQuery} */
	var $oldImg = null;
	/** @type {jQuery} */
	var $newImg = null;

	function init()
	{
		$frame.draggable(
		{
			start: function() { shell.photoMoving(); },
			stop: function() { shell.photoMoved(); }
		});
	}

	/**
	 * Update layout for current photo after window resize
	 */
	this.fit = function()
	{
		if (!Session.photo) { return; }

		_this.stopAnimation();

		// force size-recalculation
		if (_fitToScreen && Session.photo.resized) { Session.photo.resized = false; }

		layout(true);
	};

	/**
	 * Update UI to display photo and begin animations
	 * @param {Function} [callback] Method called when photo image has downloaded
	 * @return {View}
	 */
	this.show = function(callback)
	{
		shell.photoChanging();
		_this.stopAnimation();

		if (Session.photo != null)
		{
			/** @type {Placement} */
			var panTo = layout();
			/** @type {Photo} */
			var p = Session.photo;

			shell.photoChanged().photoImageChanging();

			$oldImg = $newImg;
			$newImg = $(p.image);
			$frame.append($newImg);
			$newImg.fadeOut(0).fadeIn(Enum.duration.crossfade);

			if ($oldImg)
			{
				$oldImg.fadeOut(Enum.duration.crossfade, function()
				{
					if ($oldImg) { $oldImg.remove(); $oldImg = null; }
				});
			}

			_this.changed();

			p.whenReady(function()
			{
				_this.panTo(panTo);
				shell.photoImageChanged();
				if (callback) { callback(); }
			});
		}

		return shell;
	};

	/**
	 * Shadow to implement
	 */
	this.changed = function() { };

	/**
	 * @return {PhotoMediator}
	 */
	this.stopAnimation = function()
	{
		$frame.stop(true);
		return _this;
	};

	/**
	 * Stop animations and hide image
	 * @return {PhotoMediator}
	 */
	this.hide = function()
	{
		$frame.stop(true).children('img').fadeOut();
		return _this;
	};

	/**
	 * Pan image container to new position
	 * @param {Placement} placement
	 * @return {PhotoMediator}
	 */
	this.panTo = function(placement)
	{
		if (placement) { $frame.animate(placement.clean(), Enum.duration.pan); }
		return _this;
	};


	/**
	 * @return {PhotoMediator}
	 */
	this.disableDrag = function()
	{
		$frame.css("cursor", "default").draggable("option", "disabled", true);
		return _this;
	};

	/**
	 * @param {Array.<Number>} boundary
	 * @see http://api.jqueryui.com/draggable/#option-containment
	 * @return {PhotoMediator}
	 */
	this.enableDrag = function(boundary)
	{
		$frame
			.css("cursor", "move")
			.draggable("option", "disabled", false)
			.draggable("option", "containment", boundary);

		return _this;
	};

	/**
	 * @return {PhotoMediator}
	 */
	this.toggleZoom = function()
	{
		_this.stopAnimation();

		if (_fitToScreen)
		{
			if (Session.photo.resized) { Session.photo.resetSize(); }
			_fitToScreen = false;
		}
		else
		{
			_fitToScreen = true;
		}
		shell.toolbar.toggleZoom(!_fitToScreen);
		layout(true);

		return _this;
	};

	/**
	 * Size photo and display elements for given photo
	 * @param {Boolean} [immediate = false] Whether to resize now or wait Constant.duration.resize
	 * @return {Placement} How photo should pan if it exceeds screen size
	 * @see http://api.jquery.com/animate/
	 */
	function layout(immediate)
	{
		var stripWidth = Enum.filmstrip.small + Enum.filmstrip.border;
		var maxWidth = window.innerWidth - stripWidth;
		var maxHeight = window.innerHeight;
		var duration = (immediate) ? 0 : Enum.duration.photoResize;
		/** @type {Placement} */
		var panTo = new Placement();
		/** @type {Placement} */
		var l = new Placement();
		/** @type {Boundary} */
		var boundary = new Boundary();
		/** @type {Boolean} */
		var canDrag = false;
		/** @type {Photo} */
		var p = Session.photo;

		_zoomEnabled = p.largerThan(maxWidth, maxHeight);

		// resize image before calculating placement
		if (_fitToScreen && _zoomEnabled) { p.fitToSize(maxWidth, maxHeight); }

		l.width = p.image.width;
		l.height = p.image.height;
		boundary.maxLeft = boundary.minLeft = l.left = stripWidth;
		boundary.maxTop = boundary.minTop = l.top = 0;

		if (l.width > maxWidth)
		{
			canDrag = true;
			panTo.left = boundary.minLeft = maxWidth - l.width + stripWidth;
		}

		if (l.height > maxHeight)
		{
			canDrag = true;
			panTo.top = boundary.minTop = maxHeight - l.height;
		}

		// position image box after delay
		$frame.animate(l, duration).delay(Enum.duration.photoResize);

		if (canDrag) { _this.enableDrag(boundary.toArray()); } else { _this.disableDrag(); }

		return panTo.clean();
	}

	init();
}